/**
 * promise A+ 实现
 */
class Promise {
    /**
     * 构造函数
     * @executor	执行器， 传递两个方法， resolve， reject，用以修改 promise 的状态
     **/
    constructor(executor) {
        /* 属性 */
        // 状态
        this.status = 'pending';
        // 终值
        this.value = null;
        // 据因
        this.reason = null;
        // 内部成功时候执行的队列
        this.resolvedQueue = [];
        // 内部失败是否执行的队列
        this.rejectedQueue = [];

        // 定义内部方法
        // resolve
        let resolve = value => {
            // 检测状态
            if (this.status === 'pending') {
                // 修改状态
                this.status = 'resolved';
                // 修改终值
                this.value = value;
                // 立即执行队列
                this.resolvedQueue.forEach(onResolved => onResolved(this.value))
            }
        }
        // reject
        let reject = reason => {
            // 检测状态
            if (this.status === 'pending') {
                // 修改状态
                this.status = 'rejected';
                // 修改终值
                this.reason = reason;
                // 立即执行队列
                this.rejectedQueue.forEach(onRejected => onRejected(this.reason))
            }
        }

        /* 运行执行器 */
        // 尝试运行
        try {
            // 运行
            executor(resolve, reject);
        } catch (e) {
            // 以 e 作为据因修改 promise 的状态
            reject(e);
        }
    }

    /* 原型方法 */
    /**
     * then方法 用于添加监听的回调
     * @onResolved 成功时执行的回调方法
     * @onRejected 失败时执行的回调方法
     */
    then(onResolved, onRejected) {
        // 检测类型
        onResolved = typeof onResolved === 'function' ? onResolved : x => x;
        onRejected = typeof onRejected === 'function' ? onRejected : e => {
            throw e
        };

        // 声明结果变量
        let promise;

        /* 判断状态 */
        switch (this.status) {
            // 等待态
            case 'pending':
                // 修改结果变量
                promise = new Promise((resolve, reject) => {
                    // 向成功时候执行的队列中添加回调
                    this.resolvedQueue.push(() => {
                        // 异步执行 
                        setTimeout(() => {
                            // 尝试运行
                            try {
                                // 执行获取返回值
                                let x = onResolved(this.value);
                                // 使用解决程序进行兼容处理
                                this.solutionProcedure(promise, x, resolve, reject);
                            } catch (e) {
                                // 以 e 作为据因修改 promise 
                                reject(e);
                            }
                        })
                    });
                    // 向失败时候执行的队列中添加回调
                    this.rejectedQueue.push(() => {
                        // 异步执行 
                        setTimeout(() => {
                            // 尝试运行
                            try {
                                // 执行获取返回值
                                let x = onRejected(this.reason);
                                // 使用解决程序进行兼容处理
                                this.solutionProcedure(promise, x, resolve, reject);
                            } catch (e) {
                                // 以 e 作为据因修改 promise 
                                reject(e);
                            }
                        })
                    })
                })
                break;
                // 成功态
            case 'resolved':
                // 修改结果变量
                promise = new Promise((resolve, reject) => {
                    // 异步执行
                    setTimeout(() => {
                        // 尝试运行
                        try {
                            // 执行获取返回值
                            let x = onResolved(this.value);
                            // 使用解决程序进行兼容处理
                            this.solutionProcedure(promise, x, resolve, reject);
                        } catch (e) {
                            // 以 e 作为据因修改 promise 
                            reject(e);
                        }
                    })
                })
                break;
                // 失败态
            case 'rejected':
                // 修改结果变量
                promise = new Promise((resolve, reject) => {
                    // 异步执行
                    setTimeout(() => {
                        // 尝试运行
                        try {
                            // 执行获取返回值
                            let x = onRejected(this.reason);
                            // 使用解决程序进行兼容处理
                            this.solutionProcedure(promise, x, resolve, reject);
                        } catch (e) {
                            // 以 e 作为据因修改 promise 
                            reject(e);
                        }
                    })
                })
                break;
        }

        // 直接返回 promise 
        return promise
    }

    /**
     * 解决程序，用于兼容其他遵守 A+ 规范的 promise 实现
     * @promsie		promise 的实例
     * @x			可以是 promise 实例， 也可以是 thenable，还可以是任意 js 值类型
     * @resolve		用于更改 promise 的状态
     * @reject		用于更改 promise 的状态
     */
    solutionProcedure(promise, x, resolve, reject) {
        // 判断是否全等
        if (promise === x) return reject(new TypeError('完全相同的 promise 实例'));

        // 判断是否为 promise 实例
        if (x instanceof Promise) {
            // 监听状态
            x.then(xValue => {
                // 修改 promise 的状态
                this.solutionProcedure(promise, xValue, resolve, reject);
            }, xReason => {
                // 以 xReason 作为据因修改状态
                reject(xReason);
            })
        }

        // 检测类型
        if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
            // 尝试运行
            try {
                // 假设 x 是一个 thenable
                let then = x.then;
                // 判断是不是有效的方法】
                if (typeof then === 'function') {
                    // 通过设置信号量，判断是不是第一次运行
                    let isUse = false;
                    // 尝试运行
                    try {
                        // 进行监听
                        then.call(x, y => {
                            // 检测是不是第一次运行
                            if (!isUse) {
                                // 使用解决程序进行兼容处理
                                this.solutionProcedure(promise, y, resolve, reject);
                                // 修改信号量
                                isUse = true;
                            }
                        }, r => {
                            // 检测是不是第一次运行
                            if (!isUse) {
                                // 直接使用 r 作为据因修改 promise 的状态
                                reject(r);
                                // 修改信号量
                                isUse = true;
                            }
                        })
                    } catch (e) {
                        // 以 e 作为据因修改 promise 的状态
                        isUse || reject(e);
                    }
                } else {
                    // 将 x 为终值修改 promise 状态
                    resolve(x);
                }
            } catch (e) {
                // 将 e 作为据因修改 promise 的状态
                reject(e);
            }
        } else {
            // 将 x 为终值修改 promise 状态
            resolve(x);
        }
    }
}

// 封装下述 API 用于测试
Promise.deferred = function() {
    let def = {};
    def.promise = new Promise(function(resolve, reject) {
        def.resolve = resolve;
        def.reject = reject;
    });
    return def;
}

module.exports = Promise
